﻿using System;

using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Collections;
using Silvermoon.UI;
using System.Diagnostics;

namespace Silvermoon.Controls
{
    /// <summary>
    /// Base class for all Item controls which are hosted in controls which support IItemHost (ListView, ListViewGroup, Picker,...).
    /// Item has ItemIndex, DataValue and Host property.
    /// </summary>
    public abstract class Item : ChromeControl
    {
        protected const int ISSELECTED_BIT = 1;
        protected const int ISREADONLY_BIT = 2;
        protected const int IS_PRESSED_BIT = 4;

        private static Margin itemMargin = new Margin(0, 0, VScrollBar.ScrollbarWidth, 0);

        protected int bitmask;
        private object data;
        private IItemHost itemHost;
        internal ItemPlacement ControllerPlacement;


        public Item()
            : base()
        {
            Height = 80;
        }

        protected override void Dispose(bool dispose)
        {
            DataValue = null;
            itemHost = null;
            base.Dispose(dispose);
        }

        protected override void ApplyStyle()
        {
            base.ApplyStyle();
            if (!HasMargin) Margin = itemMargin;
        }

        /// <summary>
        /// Gets the index of the data item.
        /// </summary>
        public int ItemIndex { get; internal protected set; }

        /// <summary>
        /// Gets or sets whether the item is selected.on.
        /// </summary>
        public bool IsSelected
        {
            get { return (bitmask & ISSELECTED_BIT) != 0; }
            set
            {
                if (value != IsSelected)
                {
                    bitmask = value ? bitmask | ISSELECTED_BIT : bitmask & ~ISSELECTED_BIT;
                    OnSelectionChanged(Host != null);
                }
            }
        }

        public bool IsReadonly
        {
            get { return (bitmask & ISREADONLY_BIT) != 0; }
            set
            {
                if (value != IsReadonly)
                {
                    bitmask = value ? bitmask | ISREADONLY_BIT : bitmask & ~ISREADONLY_BIT;
                    OnReadonlyChanged();
                }
            }
        }

        /// <summary>
        /// Gets the host which controls the item, otherwise null.
        /// </summary>
        public IItemHost Host
        {
            get { return itemHost; }
            protected set
            {
                if (itemHost != value)
                {
                    itemHost = value;
                    OnHostChanged();
                }
            }
        }


        /// <summary>
        /// Gets the data the item is virtualizing.
        /// </summary>
        public object DataValue
        {
            get { return data; }
            set
            {
                if (data != value)
                {
                    data = value;
                    OnDataChanged();
                }
            }
        }

        protected virtual void OnDataChanged()
        {
        }

        protected virtual void OnHostChanged()
        {
            IsSelected = Host != null ? Host.IsItemSelected(this) : false;
        }

        /// <summary>
        /// The item is released from being shown in a <see cref="ListView"/> and optional controls  may be disposed.
        /// </summary>
        internal protected virtual void Release()
        {
            Host = null;
            DataValue = null;
            ItemIndex = -1;
            RemoveAnimationStates();
            IsSelected = false;
        }

        /// <summary>
        /// Attaches the item to a host.
        /// Note that it is required to have set the Parent property of the item before, otherwise a style wouldn't be recognized correclty.
        /// </summary>
        /// <remarks>
        /// Note that it is required to have set the Parent property of the item before, otherwise a style wouldn't be recognized correclty.
        /// </remarks>
        internal protected virtual void Attach(int index, object value, IItemHost host)
        {
            CheckParent();
            if (CheckFlag(InvalidationFlag.NoStyleApplied)) PreInitialize();
            this.ItemIndex = index;
            this.DataValue = value;
            this.IsSelected = host.IsItemSelected(this);
            this.Host = host;
        }

        [Conditional("DEBUG")]
        private void CheckParent()
        {
            if (Parent == null) throw new ArgumentNullException("Parent", "Method Item.Attach() requires to have added the item to a parent before.");
        }

        /// <summary>
        /// Brings the item into the view.  
        /// Note that that item must be attached to the ListView, otherwhise an exception is raised.
        /// To bring a item into view which is not attached to a ListView, use ListView.BringItemIntoView() method.
        /// </summary>
        /// <param name="animated">True to perform animation, otherwise false.</param>
        /// Raises <exception cref="ArgumentNullException"/> if ListView is null.
        public override void BringIntoView(bool animated)
        {
            if (Host == null) throw new ArgumentNullException("ListView");
            Host.BringItemIntoView(this.ItemIndex, animated);
        }

        /// <summary>
        /// Ensures that the given rectangle is visible inside this item.
        /// Note that that item must be attached to the ListView, otherwhise an exception is raised.
        /// To bring a rectangle in an item into view which is not attached to a ListView, use ListView.BringItemRectIntoView() method.
        /// </summary>
        /// <param name="bounds">The bounds to be visible.</param>
        /// <param name="animated">True to perform animation, otherwise false.</param>
        public override void BringIntoView(Rectangle bounds, bool animated)
        {
            if (Host == null) throw new ArgumentNullException("ListView");
            Host.BringItemRectIntoView(this.ItemIndex, bounds, animated);
        }

        protected virtual void OnSelectionChanged(bool animated)
        {
            Invalidate(InvalidationFlag.Appearance);
            if (Host != null && IsSelected) Host.SelectedItemIndex = this.ItemIndex;
            string state = IsSelected ? VState.Selected : VState.Unselected;
            VisualStateManager.GoToState(this, state, IsInitialized && animated && Host != null);
            if (Host != null && SelectionChanged != null && IsInitialized) SelectionChanged(this, EventArgs.Empty);
        }

        protected virtual void OnReadonlyChanged()
        {
        }

        protected internal override void OnTap(TapEventArgs e)
        {
            base.OnTap(e);
            if (!e.Handled && Enabled)
            {

                if (Host != null)
                {
                    e.Handled = true;
                    Host.OnTapItem(this);
                }
            }
        }

        public static ItemPlacement GetPlacement(IItemHost host, int index)
        {
            if (index < 0 || host == null || host.ItemsSource == null) return ItemPlacement.Single;
            IList list = host.ItemsSource;
            if (index == 0) return (list.Count > 1) ? ItemPlacement.First : ItemPlacement.Single;
            return (list.Count == index + 1) ? ItemPlacement.Last : ItemPlacement.Middle;
        }

        public override bool CanFocus
        {
            get { return false; }
        }

        /// <summary>
        /// Occurs when IsSelected changes.
        /// </summary>
        public event EventHandler SelectionChanged;

        /// <summary>
        /// Occurs when the item got notified about changes that where made which affect the appearance of the item.
        /// </summary>
        public virtual void Update()
        {
            Invalidate(InvalidationFlag.UpdateChildren | InvalidationFlag.Layout | InvalidationFlag.Appearance);
            if (Updated != null) Updated(this, EventArgs.Empty);
        }

        /// <summary>
        /// Occurs when the item got notified about changes that where made which affect the appearance of the item.
        /// </summary>
        public event EventHandler Updated;

    }
}
